﻿namespace BM.Tools.WebControls.MForm.Generator.Templates
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Xml.Schema;

    using BM.Tools.WebControls.MForm.Controls;
    using BM.Tools.WebControls.MForm.Controls.DefaultTemplates;
    using BM.Tools.WebControls.MForm.Controls.Helper;
    
    /// <summary>
    /// This class is a template to generate web controls out of <see cref="FormItem" /> definitions.
    /// </summary>
    public class ItemTemplate : ITemplate
    {        
        /// <summary>
        /// Initializes a new instance of the <see cref="ItemTemplate"/> class. 
        /// </summary>
        /// <param name="item">
        /// The form item.
        /// </param>
        public ItemTemplate(FormItem item)
        {
            Item = item;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this is designer generation.
        /// </summary>
        public bool IsDesignerGeneration
        {
            get;
            set;
        }

        /// <summary>
        /// Gets form builder.
        /// </summary>        
        public FormBuilder Builder
        {
            get
            {
                return Item.Builder;
            }
        }

        /// <summary>
        /// Gets a value indicating whether ResourceKeys should be generated.
        /// </summary>        
        public bool GenerateResourceKeys
        {
            get
            {
                return Builder.Settings.GenerateResourceKeys;
            }
        }

        /// <summary>
        /// Gets a value indicating whether Ordinal property should be generated.
        /// </summary>
        public bool GenerateOrdinal
        {
            get
            {
                return Builder.Settings.GenerateOrdinal;
            }
        }

        /// <summary>
        /// Gets a value indicating whether leafs should be placed before the rest.
        /// </summary>
        public bool AlwaysPlaceLeafsFirst
        {
            get
            {
                return Builder.Settings.AlwaysPlaceLeafsFirst;
            }
        }

        /// <summary>
        /// Gets the item from which the template is created.
        /// </summary>        
        public FormItem Item
        {
            get;
            private set;
        }   
        
        private IEnumerable<FormItem> LeafsFirstChildrenItems
        {
            get 
            {
                return Item.ChildrenItems.Where(item => item.IsLeaf).Union(Item.ChildrenItems.Where(item => !item.IsLeaf));
            }
        }     

        /// <summary>
        /// Generates the <see cref="Offspring" /> in the given container control based on the <see cref="Item" /> definitions.
        /// </summary>
        /// <param name="container">
        /// The container.
        /// </param>
        public void InstantiateIn(Control container)
        {
            container.Controls.Add(CreateOffspring());
        }

        private static AdditionsTemplate GetAdditionsTemplate(Offspring offspring)
        {
            return offspring.Additions as AdditionsTemplate;
        }

        private ITemplate BuildChildren()
        {         
            var childrenItems = AlwaysPlaceLeafsFirst ? LeafsFirstChildrenItems : Item.ChildrenItems;
            var templates = childrenItems.Where(item => item.Enabled).Select(item => new ItemTemplate(item)).OfType<ITemplate>();            
            return new MergedTemplate(templates);
        }

        private void BuildLeaf(Leaf leaf)
        {
            if (leaf == null)
            {
                return;
            }

            if (Item is FormAttribute)
            {
                leaf.LeafType = LeafType.Attribute;
            }

            if (Item.MaxLength > Item.Builder.Settings.ForceTextareaLength)
            {
                leaf.ValueHandler = new TextBoxValueHandlerTemplate(true);
            }

            leaf.DataType = Item.DataType.Value;
            if (Item.DataType == XmlTypeCode.Boolean)
            {
                leaf.ValueHandler = new ListBoxValueHandlerTemplate();
                string[] trueFalse = { "true", "false" };
                Item.Options = new Collection<string>(trueFalse);
            }

            CreateEditableField(leaf);
        }

        private void CreateEditableField(Leaf leaf)
        {
            if (Item.DefaultValue != null)
            {
                leaf.DefaultValue = Item.DefaultValue;
            }

            if (Item.FixedValue != null)
            {
                leaf.DefaultValue = Item.FixedValue;
                leaf.ValueHandler = new LabelValueHandlerTemplate();
            } 
            else if (Item.Options.Count > 0)
            {
                var dropDownTemplate = new ListBoxValueHandlerTemplate();
                foreach (string option in Item.Options)
                {
                    var listItem = new ListItem(option, option);
                    if (Builder.Settings.ListItemCreated != null)
                    {
                        Builder.Settings.ListItemCreated(
                                this,
                                new ListItemCreationEventArgs { ListItem = listItem, OptionValue = option });
                    }

                    dropDownTemplate.AddListItem(listItem);
                }

                leaf.ValueHandler = dropDownTemplate;
            }

            if (Item.Restrictions.Count > 0)
            {
                foreach (string key in Item.Restrictions.Keys)
                {
                    GetAdditionsTemplate(leaf).AddRestriction(key, Item.Restrictions[key]);
                }
            }
        }        

        private Offspring CreateOffspring()
        {
            var offspring = InstantiateOffspring();
            EnsureGenerateResourceKeys(offspring);
            EnsureGenerateOrdinal(offspring);
            offspring.Additions = new AdditionsTemplate();
            SetMinMaxOccurs(offspring);
            CreateIdentityConstraints(offspring);
            offspring.Name = Item.QualifiedName;

            var leaf = offspring as Leaf;
            if (leaf != null)
            {
                BuildLeaf(leaf);  
                if (Builder.Settings.LeafCreated != null)
                {
                    Builder.Settings.LeafCreated(leaf, new ItemTemplateCreationEventArgs { ItemTemplate = this });
                }
            }    

            var offspringsOwner = offspring as OffspringsOwner;
            if (offspringsOwner != null)
            {
                offspringsOwner.Contents = BuildChildren();
                if (Builder.Settings.OffspringsOwnerCreated != null)
                {
                    Builder.Settings.OffspringsOwnerCreated(offspringsOwner, new ItemTemplateCreationEventArgs { ItemTemplate = this });
                }
            }

            return offspring;
        }

        private void CreateIdentityConstraints(Offspring offspring)
        {
            if (Item.IdentityConstraints.Count > 0)
            {
                foreach (XmlSchemaIdentityConstraint xsic in Item.IdentityConstraints)
                {
                    var result = GetAdditionsTemplate(offspring).CreateIdentityConstraint(xsic);
                    if (Builder.Settings.IdentityConstraintCreated != null)
                    {
                        Builder.Settings.IdentityConstraintCreated(result, new IdentityConstraintCreationEventArgs { SchemaConstraint = xsic });
                    } 
                }
            }
        }

        private void EnsureGenerateOrdinal(Offspring offspring)
        {
            if (!GenerateOrdinal)
            {
                return;
            }

            if (Item.ParentElement == null)
            {
                return;
            }

            offspring.Ordinal = Item.ParentElement.ChildrenItems.IndexOf(Item) + 1;
        }

        private void EnsureGenerateResourceKeys(Offspring offspring)
        {
            if (!GenerateResourceKeys)
            {
                return;
            }

            offspring.Attributes["meta:resourcekey"] = GetResourceKey();
        }
        
        private string GetResourceKey()
        {
            var builder = new StringBuilder();
            if (Item.ParentElement != null)
            {
                builder.Append(Item.ParentElement.QualifiedName);
                builder.Append("_");
            }

            builder.Append(Item.QualifiedName);
            return builder.ToString();
        }
        
        private Offspring InstantiateOffspring()
        {
            if (Item is FormSubstitution || Item is FormChoice)
            {
                return new Choice { IsDuringGeneration = IsDesignerGeneration };
            }

            if (Item.ChildrenItems.Count == 0 && Item.DataType != null)
            {
                return new Leaf { IsDuringGeneration = IsDesignerGeneration };
            }

            return new Branch { IsDuringGeneration = IsDesignerGeneration };
        }

        private void SetMinMaxOccurs(Offspring offspring)
        {
            offspring.MinOccurs = (int)Item.MinOccurs;
            decimal mo = Item.MaxOccurs;
            if (mo > Int32.MaxValue)
            {
                mo = Int32.MaxValue;
            }

            offspring.MaxOccurs = (int)mo;
        }
    }
}